package model;

import exception.InvalidTSException;
import exception.VMNotReadyException;
import exception.BuildNameNotSetException;
import exception.InvalidVMHostNameException;
import com.ibm.staf.STAFException;
import com.ibm.staf.STAFHandle;
import com.ibm.staf.STAFMarshallingContext;
import com.ibm.staf.STAFResult;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author pyin
 */
public class VMMachine {

    private String os;
    private String name;
    private String vmHost;
    private String vmFileName;
    private String vmSnapShot;
    private String tsProjectPath;
    private String tsProjectArchive;
    private String installerPath;
    private String tsTcPath;

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 59 * hash + (this.name != null ? this.name.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof VMMachine) {
            return this.getName().equals(((VMMachine) obj).getName());
        } else {
            return super.equals(obj);
        }
    }

    public String getInstallerArchive() throws BuildNameNotSetException {
        return this.installerPath + "\\" + ConfigManager.getBuildName() + ".zip";
    }

    /**
     * @return the os
     */
    public String getOs() {
        return os;
    }

    /**
     * @param os the os to set
     */
    public void setOs(String os) {
        this.os = os;
    }

    public void setTsProjectArchive(String name) {
        this.tsProjectArchive = name;
    }

    public String getTsProjectArchive() {
        return this.tsProjectArchive;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the vmHost
     */
    public String getVmHost() {
        return vmHost;
    }

    /**
     * @param vmHost the vmHost to set
     */
    public void setVmHost(String vmHost) {
        this.vmHost = vmHost;
    }

    /**
     * @return the vmFileName
     */
    public String getVmFileName() {
        return vmFileName;
    }

    /**
     * @param vmFileName the vmFileName to set
     */
    public void setVmFileName(String vmFileName) {
        this.vmFileName = vmFileName;
    }

    /**
     * @return the vmSnapShot
     */
    public String getVmSnapShot() {
        return vmSnapShot;
    }

    /**
     * @param vmSnapShot the vmSnapShot to set
     */
    public void setVmSnapShot(String vmSnapShot) {
        this.vmSnapShot = vmSnapShot;
    }

    public boolean hasCurrentBuildSnapshot() throws BuildNameNotSetException {
        String buildName = ConfigManager.getBuildName();
        if (buildName == null) {
            throw new BuildNameNotSetException();
        } else {
            String[] snapshots = this.getSnapshots();
            for (int i = 0; i < snapshots.length; i++) {
                if (snapshots[i].equalsIgnoreCase(buildName)) {
//                    System.out.println("Found current buildName: "+buildName);
                    return true;
                }
            }
            return false;
        }
    }

    public boolean isStarted() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.isStartedHandleName());
            STAFResult result = handle.submit2(this.name, "ping", "ping");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            }
            if (result.rc == 16) {
                return false;
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public boolean isHostingMachineReady() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.isStartedHandleName());
            STAFResult result = handle.submit2(this.vmHost, "ping", "ping");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public boolean shutdown() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getSnapshotsHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command" +
                    " \"vmrun stop \\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            }

        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public String[] getSnapshots() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getSnapshotsHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command" +
                    " \"vmrun listSnapshots \\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (!item.get("data").toString().startsWith("Error")) {
                        String returned = item.get("data").toString();
//                        System.out.println(returned);
                        return parseReturnedResult(returned);
                    }
//                    Iterator it = hash.keySet().iterator();
//                    while(it.hasNext()){
//                        String key = (String)it.next();
//                        System.out.println("["+key+"]="+hash.get(key).toString());
//                    }
                }
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public boolean revertToSnapshot(String snapshotName) {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.revertToSnapshotHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command \"vmrun revertToSnapshot " +
                    "\\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\" " + snapshotName +
                    " \" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("data").toString().startsWith("Error")) {
                        return false;
                    } else {
                        return true;
                    }
                }
            } else {
                System.out.println(result.result);
            }

        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public boolean start() {
        if (!isStarted()) {
            try {
                STAFHandle handle = new STAFHandle(ConfigManager.startVMHandleName());
                STAFResult result = handle.submit2(this.vmHost, "process", "start command \"vmrun start " +
                        "\\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
                handle.unRegister();
                if (result.rc == 0) {
                    if (STAFMarshallingContext.isMarshalledData(result.result)) {
                        STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                                result.result);
                        HashMap hash = (HashMap) mc.getRootObject();
                        HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                        if (!item.get("data").toString().startsWith("Error")) {
                            while (true) {
                                if (isStarted() && setUpSTAFVars()) {
                                    return true;
                                } else {
                                    Thread.sleep(1000);
                                    Logger.getLogger("Wait for VMMachine to start up...");
                                    System.out.println("Wait for VMMachine to start up...");
                                }
                            }
                        }
                    }
                }
            } catch (Exception ex) {
                Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
            }
            return false;
        } else {
            return true;
        }
    }

    public int setupEnv(String buildName) throws BuildNameNotSetException {
        if (!this.hasCurrentBuildSnapshot()) {
            if (!revertToSnapshot("STAF")) {
                System.out.println("complete step 1");
                return 1;
            }
            if (!start()) {
                System.out.println("complete step 2");
                return 2;
            }
            if (!createServerSideProjectArchive()) {
                System.out.println("complete step 3");
                return 3;
            }
            if (!getProjectArchive()) {
                System.out.println("complete step 4");
                return 4;
            }
            if (!extractProjectArchive()) {
                System.out.println("complete step 5");
                return 5;
            }
            if (!copyInstallerArchive()) {
                System.out.println("complete step 6");
                return 6;
            }
            if (!extractInstallerArchive()) {
                System.out.println("complete step 6");
                return 7;
            }
            if (!stop()) {
                System.out.println("complete step 8");
                return 8;
            }
            if (!takeSnapshot(buildName)) {
                System.out.println("complete step 9");
                return 9;
            }
            if (!start()) {
                System.out.println("complete step 10");
                return 10;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }


    //should not call "vmrun stop" to stop vmmachine.
    public boolean stop() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getStopVMHandleName());
            STAFResult result = handle.submit2(this.name, "process", "start command \"shutdown -s -f -t 0\"");
//            STAFResult result = handle.submit2(this.vmHost, "process", "start command \"vmrun stop " +
//                    "\\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                try {
                    Thread.sleep(120000);
                } catch (InterruptedException ex) {
                    Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
                }
                return true;
            }
        } catch (STAFException ex) {
            System.out.println(ex.getMessage());
        }
        return false;
    }

    public boolean takeSnapshot(String name) throws BuildNameNotSetException {
        try {
            if (name == null) {
                throw new BuildNameNotSetException();
            }
            STAFHandle handle = new STAFHandle(ConfigManager.getTakeSnapshotHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command " +
                    " \"vmrun snapshot \\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\" " + ConfigManager.getBuildName() + "\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("data").toString().startsWith("Error")) {
                        return false;
                    } else {
                        return true;
                    }
                }
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public void setInstallerPath(String path) {
        this.installerPath = path;
    }

    public String getTsTcPath() {
        return this.tsTcPath;
    }

    public void setTsTcPath(String path) {
        this.tsTcPath = path;
    }

    private boolean copyInstallerArchive() throws BuildNameNotSetException {
        try {
            STAFHandle handle = new STAFHandle("");
            STAFResult result = handle.submit2("local", "fs", "copy file \"" + ConfigManager.getCurrentInstallerArchive().replace("\\", "\\\\") +
                    "\" tomachine " + this.name + " todirectory \"" + this.installerPath.replace("\\", "\\\\") + "\"");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException e) {
        }
        return false;
    }

    private boolean extractInstallerArchive() throws BuildNameNotSetException {
        STAFHandle handle;
        try {
            handle = new STAFHandle("");
            STAFResult result = handle.submit2(this.name, "zip", "unzip zipfile \"" + getInstallerArchive().replace("\\", "\\\\") +
                    "\" todirectory \"" + this.installerPath.replace("\\", "\\\\") + "\" replace");
//            handle.unRegister();
            if (result.rc == 0) {
                result = handle.submit2(this.name, "fs", "delete entry \"" + getInstallerArchive().replace("\\", "\\\\") + "\" confirm");
                if (result.rc == 0) {
                    return true;
                }
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException e) {
        }
        return false;
    }

    public boolean createServerSideProjectArchive() {
        try {
            File archive = new File(ConfigManager.getProjectArchiveName());
            if (archive.exists()) {
                archive.delete();
            }
            STAFHandle handle = new STAFHandle(ConfigManager.getZipHandleName());
            STAFResult result = handle.submit2(ConfigManager.getServerHostName(), "zip", "add zipfile " + "\"" + ConfigManager.getProjectArchiveName().replace("\\", "/") +
                    "\" directory \"" + ConfigManager.getProjectPath().replace("\\", "/") + "\" relativeto \"" + ConfigManager.getProjectPath().replace("\\", "/") + "\" recurse");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;

    }

    //Copy project archive from server to remote test vm machine: local
    public boolean getProjectArchive() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getFSHandleName());
            STAFResult result = handle.submit2("local", "fs", "copy file \"" + ConfigManager.getProjectArchiveName().replace("\\", "\\\\") + "\" tomachine " + this.name + " tofile \"" + this.tsProjectArchive.replace("\\", "\\\\") + "\"");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException ex) {
            System.out.println(ex.getMessage());
        }
        return false;

    }

    public boolean extractProjectArchive() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getUnzipHandleName());
            STAFResult result = handle.submit2(this.name, "zip", "unzip zipfile \"" + this.tsProjectArchive.replace("\\", "\\\\") + "\" todirectory \"" + this.tsProjectPath.replace("\\", "\\\\") + "\" replace");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException ex) {
            System.out.println(ex.getMessage());
        }
        return false;
    }

    public boolean restore(String snapshotName) {
        return (revertToSnapshot(snapshotName) && start() && createServerSideProjectArchive() && getProjectArchive() && extractProjectArchive());
    }

    public static void main(String[] args) {
        try {
//        try {
//            VMMachine machine = VMMachineFactory.getVMMachine("win03uepinstall");
//            System.out.println(machine.setupEnv("uep10drop_milestone22"));
            VMMachine machine = VMMachineFactory.getVMMachine("win03uepinstall");
            ConfigManager.setBuildName("uep10drop_milestone22");
            System.out.println(machine.stop());
//            machine.setTsProjectArchive("e:\\test.zip");
//            machine.copyInstaller();
//            System.out.println("getProjectArchive:"+machine.getProjectArchive());
//            System.out.println("extractProjectArchive:"+machine.extractProjectArchive());


//            machine.restore(ConfigManager.getBuildName());
//            System.out.println(machine.setupEnv("m3b"));
//            System.out.println(machine.getOs());
//        } catch (BuildNameNotSetException ex) {
//            System.out.println(ex.getMessage());
//        } catch (InvalidVMHostNameException e){
//            System.out.println(e.getMessage());
//        }
        } catch (Exception ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
//            System.out.println(machine.getOs());



//        } catch (BuildNameNotSetException ex) {
//            System.out.println(ex.getMessage());
//        } catch (InvalidVMHostNameException e){
//            System.out.println(e.getMessage());
//        }

    }

    public void setTsProjectPath(String path) {
        this.tsProjectPath = path;
    }

    public String getTsProjectPath() {
        return this.tsProjectPath;
    }

    private String[] parseReturnedResult(String returned) {
        String[] result = returned.split("\r\n");
        String[] x = new String[result.length - 1];
        for (int i = 0; i < x.length; i++) {
            x[i] = result[i + 1];
        }
        return x;
    }

    public TestScript getRunningScript() throws VMNotReadyException, InvalidTSException, InvalidVMHostNameException, BuildNameNotSetException {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getListHandlesHandleName());
            STAFResult result = handle.submit2(this.vmHost, "handle", "list handles");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    LinkedList list = (LinkedList) mc.getRootObject();
                    Iterator it = list.iterator();
                    while (it.hasNext()) {
                        HashMap entry = (HashMap) it.next();
//                        System.out.println(entry.get("name"));
                        String name = entry.get("name").toString();
                        if (name.startsWith("HN_")) {
                            return TestScriptManager.getScript(name.replace("HN_", "").trim());
                        }
                    }
                }
            }
            if (result.rc == 16) {
                throw new InvalidTSException();
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    private boolean setUpSTAFVars() {
        try {
            STAFHandle handle = new STAFHandle("set_var");
            STAFResult result = handle.submit2(this.name, "var", "set shared var INTMONITOR/INSTALLER_PATH=" + this.installerPath);
            if (result.rc == 0) {
                return true;
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException e) {
            e.printStackTrace();
        }
        return false;
    }
}
